using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace PrisonStep
{
    /// <summary>
    /// This class implements one section of our prison ship
    /// </summary>
    public class PrisonModel
    {
        #region Fields

        /// <summary>
        /// The section (6) of the ship
        /// </summary>
        private int section;

        /// <summary>
        /// The name of the asset (FBX file) for this section
        /// </summary>
        private string asset;

        /// <summary>
        /// Fourth Light information.
        /// </summary>
        public Vector3 FourthColor = Vector3.Zero;
        public Vector3 FourthLocation = Vector3.Zero;
        public float FourthIntensity = 0;


        /// <summary>
        /// The game we are associated with
        /// </summary>
        private PrisonGame game;

        /// <summary>
        /// The XNA model for this part of the ship
        /// </summary>
        private Model model;

        /// <summary>
        /// To make animation possible and easy, we save off the initial (bind) 
        /// transformation for all of the model bones. 
        /// </summary>
        private Matrix[] bindTransforms;

        /// <summary>
        /// The is the transformations for all model bones, potentially after we
        /// have made some change in the tranformation.
        /// </summary>
        private Matrix[] boneTransforms;

        /// <summary>
        /// A list of all of the door bones in the model.
        /// </summary>
        private List<int> doors = new List<int>();

        /// <summary>
        /// A list of all of the door sliding amounts in the model.
        /// </summary>
        private List<float> slide = new List<float>();

        bool isOpen = false;

        /// <summary>
        /// Contains the current slime color
        /// </summary>
        private Vector3 slimeColor = new Vector3(1.0f, 1.0f, 1.0f);

        /// <summary>
        /// Indicates whether or not the slime is currently activated
        /// </summary>
        private bool slimeActivated = false;

        private Dictionary<int, float> doorHeights = new Dictionary<int, float>();
        private Dictionary<int, bool> doorOpen = new Dictionary<int, bool>();


        //doorData holds the door locations and height of each door
        //x, z, height
        private Dictionary<string, Vector3> doorData = new Dictionary<string, Vector3>();
        private Dictionary<int, int> doorToBone = new Dictionary<int, int>();
        public Dictionary<int, int> DoorToBone { get { return doorToBone; } }
        public Dictionary<int, float> DoorHeights { get { return doorHeights; } set { doorHeights = value; } }
        public Dictionary<int, bool> DoorOpen { get { return doorOpen; } set { doorOpen = value; } }
        public int Section { get { return section; } }
        public List<int> Doors { get { return doors; } }
        public bool SlimeActivated { get { return slimeActivated; } set { slimeActivated = value; } }

        public bool IsOpen { get { return isOpen; } }

        #endregion

        #region Construction and Loading

        /// <summary>
        /// Constructor. Creates an object for a section.
        /// </summary>
        /// <param name="game"></param>
        /// <param name="section"></param>
        public PrisonModel(PrisonGame game, int section)
        {
            this.game = game;
            this.section = section;
            this.asset = "AntonPhibes" + section.ToString();

            doorData.Add("R_Door1", new Vector3(218, 1023, 0));
            doorData.Add("R_Door2", new Vector3(-11, -769, 0));
            doorData.Add("R_Door3", new Vector3(587, -999, 0));
            doorData.Add("R_Door4", new Vector3(787, -763, 0));
            doorData.Add("R_Door5", new Vector3(1187, -1218, 0));
            if (section != 6)
            {
                FourthLocation = game.LightInfo(section + 1, 2);
                FourthColor = game.LightInfo(section + 1, 3);
            }
            else
            {
                FourthLocation = game.LightInfo(section - 2, 2);
                FourthColor = game.LightInfo(section - 2, 3);
            }
        }

        /// <summary>
        /// This function is called to load content into this component
        /// of our game.
        /// </summary>
        /// <param name="content">The content manager to load from.</param>
        public void LoadContent(ContentManager content)
        {

            // Load the second model
            model = content.Load<Model>(asset);

            // Save off all of the bone information
            int boneCnt = model.Bones.Count;
            bindTransforms = new Matrix[boneCnt];
            boneTransforms = new Matrix[boneCnt];

            model.CopyBoneTransformsTo(bindTransforms);
            model.CopyBoneTransformsTo(boneTransforms);

            // Find all of the doors and save the index for the bone
            for (int b = 0; b < boneCnt; b++)
            {
                if (model.Bones[b].Name.StartsWith("DoorInner") || model.Bones[b].Name.StartsWith("DoorOuter"))
                {
                    doors.Add(b);
                    doorToBone.Add(Convert.ToInt32(model.Bones[b].Name.Substring(
                        model.Bones[b].Name.Length - 1, 1)), doors.Count - 1);
                    doorHeights.Add(Convert.ToInt32(model.Bones[b].Name.Substring(
                        model.Bones[b].Name.Length - 1, 1)), 0); //all doors are initially closed
                    doorOpen.Add(Convert.ToInt32(model.Bones[b].Name.Substring(
                        model.Bones[b].Name.Length - 1, 1)), false); //all doors are initially closed
                }
            }
        }

        #endregion

        #region Update and Draw

        /// <summary>
        /// This function is called to update this component of our game
        /// to the current game time.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            
        }

        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {

            DrawModel(graphics, model, Matrix.Identity);
        }

        private void DrawModel(GraphicsDeviceManager graphics, Model model, Matrix world)
        {
            // Apply the bone transforms
            Matrix[] absoTransforms = new Matrix[model.Bones.Count];
            model.CopyBoneTransformsFrom(boneTransforms);
            model.CopyAbsoluteBoneTransformsTo(absoTransforms);
            
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (Effect effect in mesh.Effects)
                {
                    effect.Parameters["World"].SetValue(absoTransforms[mesh.ParentBone.Index] * world);
                    effect.Parameters["View"].SetValue(game.Camera.View);
                    effect.Parameters["Projection"].SetValue(game.Camera.Projection);
                    effect.Parameters["Light4Location"].SetValue(FourthLocation);
                    effect.Parameters["Light4Color"].SetValue(FourthColor * FourthIntensity);
                    effect.Parameters["Greeness"].SetValue(game.Greeness);
                }
                mesh.Draw();
            }
        }

        #endregion

        public Vector3 getDoorData(int d)
        {
            return doorData["R_Door" + d];
        }

        public bool openDoor(int d, float delta)
        {

            if (doorHeights[d] == 200)
                doorOpen[d] = true;

            int value;
            if (doorToBone.TryGetValue(d, out value))
            {
                float changeAmnt = (delta / 2f) * 200f;
                doorHeights[d] += changeAmnt;
                if (doorHeights[d] > 200)
                    doorHeights[d] = 200;

                FourthIntensity = doorHeights[d] / 200;

                boneTransforms[doors[value]] = Matrix.CreateTranslation(0, doorHeights[d], 0) * bindTransforms[doors[value]];
            }

            return doorOpen[d];
        }

        public void closeDoor(int d, float delta)
        {
            doorOpen[d] = false;
            if (doorHeights[d] == 0)
                return;
            int value;
            if (doorToBone.TryGetValue(d, out value))
            {
                doorHeights[d] -= (delta / 2f) * 200;
                if (doorHeights[d] < 0)
                    doorHeights[d] = 0;

                FourthIntensity = doorHeights[d] / 200;

                boneTransforms[doors[value]] = Matrix.CreateTranslation(0, doorHeights[d], 0) * bindTransforms[doors[value]];
            }
        }

    }
}
